package yd.pr;

import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.res.Configuration;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.RoundRectShape;
import android.media.ThumbnailUtils;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.provider.MediaStore;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Toast;

import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

/**
 * created by zeng_yong_chang@163.com
 */
public class Util {

    public static class Converter {

        public static int dp2px(int dp, Context context) {
            return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, getDisplayMetrics(context));
        }

        public static float px2dp(float px, Context context) {
            return px / TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getDisplayMetrics(context));
        }

        public static byte[] bitmap2ByteArray(Bitmap bitmap) {
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
            return baos.toByteArray();
        }

        public static Bitmap byteArray2Bitmap(byte[] byteArray) {
            return BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
        }

        public static String imageUri2Path(Context context, Uri contentUri) {
            Cursor c = context.getContentResolver().query(contentUri, null, null, null, null);
            c.moveToFirst();
            String mediaFilePath = c.getString(c.getColumnIndex(MediaStore.Images.Media.DATA));
            c.close();
            return mediaFilePath;
        }
    }

    public static class ReflectionUtil {

        public static class ParamInfo {
            public Class<?> type;
            public Object value;

            public ParamInfo(Class<?> type, Object value) {
                this.type = type;
                this.value = value;
            }
        }

        public static Object invokeMethod(Object receiver, String methodName, ParamInfo... paramInfos) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            return invokeMethodInternal(receiver, receiver.getClass(), methodName, paramInfos);
        }

        public static Object invokeStaticMethod(String className, String methodName, ParamInfo... paramInfos) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            return invokeMethodInternal(null, Class.forName(className), methodName, paramInfos);
        }

        private static Object invokeMethodInternal(Object receiver, Class<?> clazz, String methodName, ParamInfo... paramInfos) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
            Class<?>[] paramTypes = new Class<?>[paramInfos.length];
            Object[] paramValues = new Object[paramInfos.length];
            for (int i = 0; i < paramInfos.length; i++) {
                paramTypes[i] = paramInfos[i].type;
                paramValues[i] = paramInfos[i].value;
            }
            Method method = clazz.getDeclaredMethod(methodName, paramTypes);
            method.setAccessible(true);
            return method.invoke(receiver, paramValues);
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////

        public static Object newInstance(String className, Class[] paramTypes, Object[] paramValues) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, InstantiationException, IllegalAccessException, InvocationTargetException {
            Class clazz = Class.forName(className);
            Constructor constructor = clazz.getDeclaredConstructor(paramTypes);
            constructor.setAccessible(true);
            return constructor.newInstance(paramValues);
        }

        // invoke no-argument-constructor
        public static Object newInstance(String className) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
            Class clazz = Class.forName(className);
            return clazz.newInstance();
        }

        //////////////////////////////////////////////////////////////////////////////////////////////////

        public static void setValue(Object instance, String fieldName, Object value) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
            Field field = getAccessibleField(instance.getClass(), fieldName);
            field.set(instance, value);
        }

        public static Object getValue(Object instance, String fieldName) throws SecurityException, NoSuchFieldException, IllegalArgumentException, IllegalAccessException {
            Field field = getAccessibleField(instance.getClass(), fieldName);
            return field.get(instance);
        }

        private static Field getAccessibleField(Class clazz, String fieldName) throws NoSuchFieldException {
            Field field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
            return field;
        }
    }

    public static class HandlerUtil {

        public static void sendMsg(Handler handler, int msgCode, Bundle bundle) {
            Message msg = handler.obtainMessage(msgCode);
            if (bundle != null) {
                msg.setData(bundle);
            }
            handler.sendMessage(msg);
        }
    }

    public static class FileUtil {

        /**
         * @param path : can be path of file or directory.
         */
        public static long getFileSize(String path) {
            return getFileSize(new File(path));
        }

        /**
         * @param file : can be file or directory.
         */
        public static long getFileSize(File file) {
            if (file.exists()) {
                if (file.isDirectory()) {
                    long size = 0;
                    for (File subFile : file.listFiles()) {
                        size += getFileSize(subFile);
                    }
                    return size;
                } else {
                    return file.length();
                }
            } else {
                throw new IllegalArgumentException("File does not exist!");
            }
        }

        public static void copyFile(String originalFilePath, String destFilePath) throws IOException {
            copyFile(new File(originalFilePath), destFilePath);
        }

        public static void copyFile(String originalFilePath, File destFile) throws IOException {
            copyFile(new File(originalFilePath), destFile);
        }

        public static void copyFile(File originalFile, String destFilePath) throws IOException {
            copyFile(originalFile, new File(destFilePath));
        }

        public static void copyFile(File originalFile, File destFile) throws IOException {
            copy(new FileInputStream(originalFile), new FileOutputStream(destFile));
        }

        public static void copy(InputStream inputStream, OutputStream outputStream) throws IOException {
            byte buf[] = new byte[1024];
            int numRead = 0;

            while ((numRead = inputStream.read(buf)) != -1) {
                outputStream.write(buf, 0, numRead);
            }

            close(outputStream, inputStream);
        }

        /**
         * @param path : can be file's absolute path or directories' path.
         */
        public static void deleteFile(String path) {
            deleteFile(new File(path));
        }

        /**
         * @param file : can be file or directory.
         */
        public static void deleteFile(File file) {
            if (!file.exists()) {
                LogUtil.e("The file to be deleted does not exist! File's path is: " + file.getPath());
            } else {
                deleteFileRecursively(file);
            }
        }

        /**
         * Invoker must ensure that the file to be deleted exists.
         */
        private static void deleteFileRecursively(File file) {
            if (file.isDirectory()) {
                for (File item : file.listFiles()) {
                    if (item.isDirectory()) {
                        deleteFileRecursively(item);
                    } else {
                        if (!item.delete()) {
                            LogUtil.e("Failed in recursively deleting a file, file's path is: " + item.getPath());
                        }
                    }
                }
                if (!file.delete()) {
                    LogUtil.e("Failed in recursively deleting a directory, directories' path is: " + file.getPath());
                }
            } else {
                if (!file.delete()) {
                    LogUtil.e("Failed in deleting this file, its path is: " + file.getPath());
                }
            }
        }

        public static String readToString(Reader in) throws IOException {
            BufferedReader reader = new BufferedReader(in);
            StringBuffer buffer = new StringBuffer();
            String readLine = null;
            while ((readLine = reader.readLine()) != null) {
                buffer.append(readLine);
                buffer.append("\n");
            }
            reader.close();
            return buffer.toString();
        }

        public static String readToString(File file) throws IOException {
            return readToString(new FileReader(file));
        }

        public static String readToString(InputStream is) throws IOException {
            return readToString(new InputStreamReader(is));
        }

        public static byte[] readToByteArray(File file) throws IOException {
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream(1024);
            copy(new FileInputStream(file), outputStream);
            return outputStream.toByteArray();
        }

        public static void writeByteArray(byte[] byteArray, FileOutputStream fos) throws IOException {
            BufferedOutputStream outputStream = new BufferedOutputStream(fos);
            outputStream.write(byteArray);
            outputStream.close();
        }

        public static void writeByteArray(byte[] byteArray, String filePath) throws IOException {

        }

        public static void writeString(String content, String filePath) throws IOException {
            writeByteArray(content.getBytes(), filePath);
        }

        public static void writeString(String content, FileOutputStream fos) throws IOException {
            writeByteArray(content.getBytes(), fos);
        }

        public static void writeString(String content, File file) throws IOException {
            writeString(content, file.getPath());
        }

        public static void saveBitmapToFile(String fileForSaving, Bitmap bitmap) {
            File targetFile = new File(Environment.getExternalStorageDirectory().getPath() + "/" + fileForSaving + ".png");
            if (!targetFile.exists()) {
                try {
                    targetFile.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            try {
                FileOutputStream fos = new FileOutputStream(targetFile);
                bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
                fos.flush();
                fos.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * If sequentialPathStrs are "a","b","c", below method return "a/b/c"
         */
        public static String toCatenatedPath(String... sequentialPathStrs) {
            String catenatedPath = "";
            for (int i = 0; i < sequentialPathStrs.length - 1; i++) {
                catenatedPath += sequentialPathStrs[i] + File.separator;
            }
            catenatedPath += sequentialPathStrs[sequentialPathStrs.length - 1];
            return catenatedPath;
        }
    }

    public static class DateUtils {

        public static String formatDateTime(Date date) {
            String text;
            long dateTime = date.getTime();
            if (isSameDay(dateTime)) {
                Calendar calendar = GregorianCalendar.getInstance();
                if (inOneMinute(dateTime, calendar.getTimeInMillis())) {
                    return "刚刚";
                } else if (inOneHour(dateTime, calendar.getTimeInMillis())) {
                    return String.format("%d分钟之前", Math.abs(dateTime - calendar.getTimeInMillis()) / 60000);
                } else {
                    calendar.setTime(date);
                    int hourOfDay = calendar.get(Calendar.HOUR_OF_DAY);
                    if (hourOfDay > 17) {
                        text = "晚上 hh:mm";
                    } else if (hourOfDay >= 0 && hourOfDay <= 6) {
                        text = "凌晨 hh:mm";
                    } else if (hourOfDay > 11 && hourOfDay <= 17) {
                        text = "下午 hh:mm";
                    } else {
                        text = "上午 hh:mm";
                    }
                }
            } else if (isYesterday(dateTime)) {
                text = "昨天 HH:mm";
            } else if (isSameYear(dateTime)) {
                text = "M月d日 HH:mm";
            } else {
                text = "yyyy-M-d HH:mm";
            }

            // 注意，如果使用android.text.format.DateFormat这个工具类，在API 17之前它只支持adEhkMmszy
            return new SimpleDateFormat(text, Locale.CHINA).format(date);
        }

        private static boolean inOneMinute(long time1, long time2) {
            return Math.abs(time1 - time2) < 60000;
        }

        private static boolean inOneHour(long time1, long time2) {
            return Math.abs(time1 - time2) < 3600000;
        }

        private static boolean isSameDay(long time) {
            long startTime = floorDay(Calendar.getInstance()).getTimeInMillis();
            long endTime = ceilDay(Calendar.getInstance()).getTimeInMillis();
            return time > startTime && time < endTime;
        }

        private static boolean isYesterday(long time) {
            Calendar startCal;
            startCal = floorDay(Calendar.getInstance());
            startCal.add(Calendar.DAY_OF_MONTH, -1);
            long startTime = startCal.getTimeInMillis();

            Calendar endCal;
            endCal = ceilDay(Calendar.getInstance());
            endCal.add(Calendar.DAY_OF_MONTH, -1);
            long endTime = endCal.getTimeInMillis();
            return time > startTime && time < endTime;
        }

        private static boolean isSameYear(long time) {
            Calendar startCal;
            startCal = floorDay(Calendar.getInstance());
            startCal.set(Calendar.MONTH, Calendar.JANUARY);
            startCal.set(Calendar.DAY_OF_MONTH, 1);
            return time >= startCal.getTimeInMillis();
        }

        private static Calendar floorDay(Calendar startCal) {
            startCal.set(Calendar.HOUR_OF_DAY, 0);
            startCal.set(Calendar.MINUTE, 0);
            startCal.set(Calendar.SECOND, 0);
            startCal.set(Calendar.MILLISECOND, 0);
            return startCal;
        }

        private static Calendar ceilDay(Calendar endCal) {
            endCal.set(Calendar.HOUR_OF_DAY, 23);
            endCal.set(Calendar.MINUTE, 59);
            endCal.set(Calendar.SECOND, 59);
            endCal.set(Calendar.MILLISECOND, 999);
            return endCal;
        }
    }

    public static class ZipUtil {

        public static void unZip(String srcPath, String destPath) {
            unZip(new File(srcPath), new File(destPath));
        }

        public static void unZip(File srcFile, File destFile) {
            final int bufferSize = 4096;
            ZipEntry entry;
            BufferedOutputStream bos;

            try {
                FileInputStream fisForCount = new FileInputStream(srcFile);
                ZipInputStream zisForCount = new ZipInputStream(new BufferedInputStream(fisForCount));
                int totalEntryCnt = 0;
                while (zisForCount.getNextEntry() != null) {
                    totalEntryCnt++;
                }
                zisForCount.close();

                FileInputStream fis = new FileInputStream(srcFile);
                ZipInputStream zis = new ZipInputStream(new BufferedInputStream(fis));
                int currentEntryCnt = 0;
                while ((entry = zis.getNextEntry()) != null) {
                    File entryFile = new File(destFile, entry.getName());
                    File entryDir = entryFile.getParentFile();
                    if (!entryDir.exists()) {
                        entryDir.mkdirs();
                    }
                    if (!entry.isDirectory()) {
                        bos = new BufferedOutputStream(new FileOutputStream(entryFile), bufferSize);
                        int numRead;
                        byte data[] = new byte[bufferSize];
                        while ((numRead = zis.read(data, 0, bufferSize)) != -1) {
                            bos.write(data, 0, numRead);
                        }
                        bos.flush();
                        bos.close();
                    }
                    currentEntryCnt++;
                    LogUtil.i("unzipped entry: " + entry);
                    LogUtil.i("unzip percentage = " + ((float) currentEntryCnt * 100 / totalEntryCnt) + "%");
                }
                zis.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        /**
         * from "ssdy"
         */
        public static void zipFolder(String srcFolderPath, String destPath)
                throws Exception {
            File srcFolder = new File(srcFolderPath);
            if (!srcFolder.isDirectory()) {
                return;
            }
            File destFile = new File(destPath);
            File parentDir = destFile.getParentFile();
            if (!parentDir.exists()) {
                parentDir.mkdirs();
            }
            ZipOutputStream zos = new ZipOutputStream(new FileOutputStream(destFile));
            for (File f : srcFolder.listFiles()) {
                BufferedInputStream bis = new BufferedInputStream(new FileInputStream(f));
                zos.putNextEntry(new ZipEntry(srcFolder.getName() + File.separator + f.getName()));
                int length;
                byte[] buffer = new byte[8192];
                while ((length = bis.read(buffer)) != -1) {
                    zos.write(buffer, 0, length);
                }
                bis.close();
            }
            zos.close();
        }
    }

    public static class TimeUtil {

        public static String format(String pattern, long milliseconds) {
            return (new SimpleDateFormat(pattern)).format(new Date(milliseconds));
        }

        /**
         * 将以毫秒为单位的时间转化为“小时：分钟：秒”的格式（不足1小时的没有小时部分）。
         * 适用于显示一段视频的时长（有些视频时长是大于1小时，有些是不足1小时的）
         */
        public static String formatHMS(long millis) {
            final long millisOfOneHour = TimeUnit.HOURS.toMillis(1);

            if (millis < millisOfOneHour) {
                return String.format("%1$tM:%1$tS", millis);
            } else {
                return String.format("%1$d:%2$TM:%2$TS", millis / millisOfOneHour, millis % millisOfOneHour);
            }
        }

        // 试验用
        public static File createRandomNowFile() {
            return createRandomNowFile(Environment.getExternalStorageDirectory(), null, null);
        }

        public static File createRandomNowFile(String prefix, String suffix) {
            return createRandomNowFile(Environment.getExternalStorageDirectory(), prefix, suffix);
        }

        public static File createRandomNowFile(String subDir, String prefix, String suffix) {
            return createRandomNowFile(new File(Environment.getExternalStorageDirectory() + "/" + subDir), prefix, suffix);
        }

        public static File createRandomNowFile(File parentDir, String prefix, String suffix) {
            try {
                parentDir.mkdirs();
                String fileName = format("mm:ss", System.currentTimeMillis());
                if (prefix != null) {
                    fileName = prefix + fileName;
                }
                if (suffix != null) {
                    fileName += ("." + suffix);
                }
                File file = new File(parentDir, fileName);
                file.createNewFile();
                return file;
            } catch (IOException e) {
                e.printStackTrace();
                return null;
            }
        }
    }

    public static class BitmapUtil {

        /**
         * 此函数用于提取缩略图，以节省内存。
         * l
         *
         * @param path
         * @param loseLessQuality 是否较少地丢失图片质量 如果为true表示按宽度和高度的缩小倍数中的较少者进行采样。
         */
        public static Bitmap sample(boolean isFromAssets, String path, int requireWidth, int requireHeight,
                                    boolean loseLessQuality) {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inPurgeable = true;
            options.inInputShareable = true;

            /**
             * "options.inJustDecodeBounds = true"这步是为了设置options.inSampleSize
             */
            options.inJustDecodeBounds = true;
            InputStream is = null;
            try {
                is = new FileInputStream(path);
            } catch (FileNotFoundException e) {
                return null;
            }
            BitmapFactory.decodeStream(is, null, options);
            int widthMinification = (int) Math.ceil((float) options.outWidth / (float) requireWidth);
            int heightMinification = (int) Math.ceil((float) options.outHeight / (float) requireHeight);

            if (loseLessQuality) {
                options.inSampleSize = Math.min(widthMinification, heightMinification);
            } else {
                options.inSampleSize = Math.max(widthMinification, heightMinification);
            }
            close(is);

            /**
             * 这一步做真正的提取缩略图
             */
            options.inJustDecodeBounds = false;
            try {
                is = new FileInputStream(path);
            } catch (FileNotFoundException e) {
                return null;
            }
            Bitmap bitmap = BitmapFactory.decodeStream(is, null, options);
            close(is);

            return bitmap;
        }

        /**
         * 根据指定的图像路径和大小来获取缩略图
         * 此方法有两点好处：
         * 1. 使用较小的内存空间，第一次获取的bitmap实际上为null，只是为了读取宽度和高度，
         * 第二次读取的bitmap是根据比例压缩过的图像，第三次读取的bitmap是所要的缩略图。
         * 2. 缩略图对于原图像来讲没有拉伸，这里使用了2.2版本的新工具ThumbnailUtils，使
         * 用这个工具生成的图像不会被拉伸。
         *
         * @param imagePath 图像的路径
         * @param width     指定输出图像的宽度
         * @param height    指定输出图像的高度
         * @return 生成的缩略图
         */
        public static Bitmap getImageThumbnail(String imagePath, int width, int height) {
            BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            // 获取这个图片的宽和高，注意此处的bitmap为null
            BitmapFactory.decodeFile(imagePath, options);

            // 重新读入图片，读取缩放后的bitmap，注意这次要把options.inJustDecodeBounds 设为 false
            options.inJustDecodeBounds = false; // 设为 false
            // 计算缩放比
            int h = options.outHeight;
            int w = options.outWidth;
            int beWidth = w / width;
            int beHeight = h / height;
            int be = 1;
            if (beWidth < beHeight) {
                be = beWidth;
            } else {
                be = beHeight;
            }
            if (be <= 0) {
                be = 1;
            }
            options.inSampleSize = be;
            Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
            // 利用ThumbnailUtils来创建缩略图，这里要指定要缩放哪个Bitmap对象
            bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
                    ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
            return bitmap;
        }

        /**
         * 获取视频的缩略图
         * 先通过ThumbnailUtils来创建一个视频的缩略图，然后再利用ThumbnailUtils来生成指定大小的缩略图。
         * 如果想要的缩略图的宽和高都小于MICRO_KIND，则类型要使用MICRO_KIND作为kind的值，这样会节省内存。
         *
         * @param videoPath 视频的路径
         * @param width     指定输出视频缩略图的宽度
         * @param height    指定输出视频缩略图的高度度
         * @param kind      参照MediaStore.Images.Thumbnails类中的常量MINI_KIND和MICRO_KIND。
         *                  其中，MINI_KIND: 512 x 384，MICRO_KIND: 96 x 96
         * @return 指定大小的视频缩略图
         */
        public static Bitmap getVideoThumbnail(String videoPath, int width, int height, int kind) {
            Bitmap bitmap;
            // 获取视频的缩略图
            bitmap = ThumbnailUtils.createVideoThumbnail(videoPath, kind);
            bitmap = ThumbnailUtils.extractThumbnail(bitmap, width, height,
                    ThumbnailUtils.OPTIONS_RECYCLE_INPUT);
            return bitmap;
        }
    }

    public static class SdcardUtil {

        public static boolean isSdcardReadable() {
            return Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED);
        }

        public static boolean checkIsSdcardFull(IOException e) {
            /** 在应用层通过异常判断是否是SD卡已满，除了下面的方式，目前尚未找到更好的方法。 **/
            return e.getMessage().contains("No space left on device");
        }
    }

    public static class Notifier {

        public static void showLongToast(CharSequence content, Context cxt) {
            Toast.makeText(cxt, content, Toast.LENGTH_LONG).show();
        }

        public static void showShortToast(CharSequence content, Context cxt) {
            Toast.makeText(cxt, content, Toast.LENGTH_SHORT).show();
        }

        public static void showLongToast(int resId, Context cxt) {
            showLongToast(cxt.getResources().getText(resId), cxt);
        }

        public static void showShortToast(int resId, Context cxt) {
            showShortToast(cxt.getResources().getText(resId), cxt);
        }
    }

    public static class GraphUtil {

        /**
         * this method is used more frequently
         */
        public static ShapeDrawable createRoundCornerShapeDrawable(float radius, int color) {
            float[] outerRadii = new float[8];
            for (int i = 0; i < 8; i++) {
                outerRadii[i] = radius;
            }

            ShapeDrawable sd = new ShapeDrawable(new RoundRectShape(outerRadii, null, null));
            sd.getPaint().setColor(color);

            return sd;
        }

        public static ShapeDrawable createRoundCornerShapeDrawableWithBoarder(float radius, float borderLength, int borderColor) {
            float[] outerRadii = new float[8];
            float[] innerRadii = new float[8];
            for (int i = 0; i < 8; i++) {
                outerRadii[i] = radius + borderLength;
                innerRadii[i] = radius;
            }

            ShapeDrawable sd = new ShapeDrawable(new RoundRectShape(outerRadii, new RectF(borderLength, borderLength,
                    borderLength, borderLength), innerRadii));
            sd.getPaint().setColor(borderColor);

            return sd;
        }

        /**
         * @param strokeWidth "stroke"表示描边, 传小于或等于0的值表示没有描边，同时忽略传入的strokeColor参数。
         */
        public static GradientDrawable createGradientDrawable(int fillColor, int strokeWidth, int strokeColor, float cornerRadius) {
            GradientDrawable gd = new GradientDrawable();

            gd.setColor(fillColor);
            if (strokeWidth > 0) {
                gd.setStroke(strokeWidth, strokeColor);
            }
            gd.setCornerRadius(cornerRadius);

            return gd;
        }
    }

    public static class AnimUtil {

        public static void alphaAnim(View v, long duration, float animateToValue) {
            v.animate().setDuration(duration).alpha(animateToValue);
        }
    }

    public static class LogUtil {

        private static final String TAG = LogUtil.class.getPackage().getName();

        /**
         * Define your log level for printing out detail here. *
         */
        private static final int PRINT_DETAIL_LOG_LEVEL = Log.ERROR;

        public static void v(Object object) {
            Log.v(TAG, buildMsg(Log.INFO, object));
        }

        public static void d(Object object) {
            Log.d(TAG, buildMsg(Log.INFO, object));
        }

        public static void i(Object object) {
            Log.i(TAG, buildMsg(Log.INFO, object));
        }

        public static void w(Object object) {
            Log.w(TAG, buildMsg(Log.INFO, object));
        }

        public static void e(Object object) {
            Log.e(TAG, buildMsg(Log.ERROR, object));
        }

        private static String buildMsg(int logLevel, Object object) {
            StringBuilder buffer = new StringBuilder();

            if (logLevel >= PRINT_DETAIL_LOG_LEVEL) {
                StackTraceElement stackTraceElement = Thread.currentThread().getStackTrace()[4];
                buffer.append("[ thread name is ");
                buffer.append(Thread.currentThread().getName());
                buffer.append(", ");
                buffer.append(stackTraceElement.getFileName());
                buffer.append(", method is ");
                buffer.append(stackTraceElement.getMethodName());
                buffer.append("(), at line ");
                buffer.append(stackTraceElement.getLineNumber());
                buffer.append(" ]");
                buffer.append("\n");
            }

            buffer.append("___");
            buffer.append(object);

            return buffer.toString();
        }
    }

    public static class TextUtil {

        public static String getEditTextContent(EditText editText) {
            return editText.getText().toString();
        }

        public static boolean isChinese(char c) {
            Character.UnicodeBlock ub = Character.UnicodeBlock.of(c);
            if (ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS
                    || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_IDEOGRAPHS
                    || ub == Character.UnicodeBlock.CJK_UNIFIED_IDEOGRAPHS_EXTENSION_A
                    || ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
                    || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
                    || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS) {
                return true;
            }
            return false;
        }

        public static boolean isEnglish(char c) {
            return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
        }
    }

    public static class NetworkUtil {

        public static boolean isNetworkAvailable(Context context) {
            return isAvailable(getConnectivityManager(context).getActiveNetworkInfo());
        }

        public static boolean isWifiAvailable(Context context) {
            return isNetworkAvailable(context, ConnectivityManager.TYPE_WIFI);
        }

        public static boolean isMobileAvailable(Context context) {
            return isNetworkAvailable(context, ConnectivityManager.TYPE_MOBILE);
        }

        private static boolean isNetworkAvailable(Context context, int type) {
            return isAvailable(getConnectivityManager(context).getNetworkInfo(type));
        }

        public static ConnectivityManager getConnectivityManager(Context context) {
            return (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
        }

        private static boolean isAvailable(NetworkInfo ni) {
            return (ni != null) && ni.isAvailable() && ni.isConnected();
        }
    }

    // >>>
    public static void setEnableForChildrenView(ViewGroup parent, boolean enabled) {
        for (int i = 0; i < parent.getChildCount(); ++i) {
            View child = parent.getChildAt(i);
            child.setEnabled(enabled);
            if (child instanceof ViewGroup) {
                setEnableForChildrenView((ViewGroup) child, enabled);
            }
        }
    }

    public static View getContentView(Activity activity) {
        return activity.getWindow().getDecorView().getRootView();
    }

    public static void freezeContentView(Activity activity) {
        setEnableForChildrenView(((ViewGroup) getContentView(activity)), false);
    }
    // <<<

    public static boolean isInsideView(MotionEvent event, View v) {
        Rect rect = new Rect();
        v.getGlobalVisibleRect(rect);

        return rect.contains((int) event.getRawX(), (int) event.getRawY());
    }

    public static void close(Closeable... closeables) {
        for (Closeable closeable : closeables) {
            if (closeable != null) {
                try {
                    closeable.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static boolean isExistedInDb(ContentResolver resolver, Uri uri, String selection, String[] selectionArgs) {
        Cursor c = resolver.query(uri, null, selection, selectionArgs, null);
        boolean isExistedInDb = (c != null && c.getCount() > 0);
        if (c != null) {
            c.close();
        }
        return isExistedInDb;
    }

    public static DisplayMetrics getDisplayMetrics(Context context) {
        return context.getResources().getDisplayMetrics();
    }

    public static void hideInputMethodPanel(View v) {
        ((InputMethodManager) v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE))
                .hideSoftInputFromWindow(v.getWindowToken(), 0);
    }

    public static String getEditTextContent(EditText editText) {
        return editText.getText().toString();
    }

    public static boolean isTablet(Context context) {
        return ((context.getResources().getConfiguration().screenLayout & Configuration.SCREENLAYOUT_SIZE_MASK))
                >= Configuration.SCREENLAYOUT_SIZE_LARGE;
    }
}
